/*
 * @Author: your name
 * @Date: 2021-08-13 22:24:54
 * @LastEditTime: 2021-08-13 22:52:15
 * @LastEditors: Please set LastEditors
 * @Description: In User Settings Edit
 * @FilePath: /面向过程io复用/poll.cpp
 */

#include <sys/socket.h>//socket基础api
#include <sys/types.h>
#include <string.h>//bzero
#include <fcntl.h>//fcntl

#include <sys/poll.h>//select
#include <netinet/in.h>//socket地址类型
#include <unistd.h>

#include <vector>
#include <iostream>
using namespace std;

#define exit_if(r, ...) if(r) {printf(__VA_ARGS__); printf("error no: %d error msg %s\n", errno, strerror(errno)); exit(1);}
using PollList=vector<pollfd>;

void SetReuse(int fd)
{
    int reuse=1;
    setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&reuse,(socklen_t)sizeof reuse);
}
void setNonBlock(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    exit_if(flags<0, "fcntl failed");
    int r = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
    exit_if(r<0, "fcntl failed");
}

int main()
{
    short port=9999;
    struct sockaddr_in seraddr;
    struct sockaddr_in cliaddr;
    socklen_t addrlen=sizeof cliaddr;
    bzero(&seraddr,sizeof seraddr);
    bzero(&cliaddr,sizeof cliaddr);
    seraddr.sin_addr.s_addr=INADDR_ANY;
    seraddr.sin_family=AF_INET;
    seraddr.sin_port=htons(port);

    int lfd=socket(AF_INET,SOCK_STREAM,0);
    exit_if(lfd==-1,"socket()错误");
    setNonBlock(lfd);
    SetReuse(lfd);
    exit_if(::bind(lfd,(const sockaddr*)&seraddr,(socklen_t)sizeof seraddr)==-1,"bind()错误");
    exit_if(listen(lfd,100)==-1,"listen()错误");

    int ret,size,nready=0;
    char buf[1024];//应用层接受缓冲区
    int len=0;

    PollList fds;
    pollfd temp;
    bzero(&temp,sizeof temp);
    temp.fd=lfd;
    temp.events=POLL_IN;
    fds.push_back(temp);

    struct timeval time,timetemp;
    time.tv_sec=5;
    time.tv_usec=0;
    timetemp=time;
    for(;;)
    {
        //每次循环之前的动作
        memset(buf,'\0',sizeof buf);
        timetemp=time;
        size=fds.size();

        nready=poll(&*fds.begin(),size,timetemp.tv_sec*1000);
        //cout<<"nready= "<<nready<<",maxfd= "<<maxfd<<endl;
        if(nready==0)
        {
            cout<<timetemp.tv_sec<<"s内暂无事件发生"<<endl;
            continue;
        }
        else if(nready==-1)
        {
            exit_if(1,"select()错误");
        }
        for(PollList::iterator it=fds.begin();it!=fds.end();it++)
        {
            int fd=it->fd;
            if(nready==0)
                break;
            else if(fd==lfd&&(it->revents&POLLIN))
            {
                ret=accept(lfd,(struct sockaddr*)&cliaddr,&addrlen);
                exit_if(ret==-1,"accept()错误");
                temp.events=POLLIN;
                temp.fd=ret;
                fds.push_back(temp);
                nready--;
            }
            else if(it->revents&POLLIN)
            {
                len=recv(fd,buf,sizeof buf,0);
                exit_if(len==-1,"read()错误");
                if(len==0)//*********
                {
                    close(fd);
                    fds.erase(it);
                    it--;
                    continue;
                }
                cout<<"read "<<len<<" bytes: "<<buf<<endl;//*****telnet的问题，/r/n
                send(fd,buf,len,0);
                nready--;
            }
        }
    }
    close(lfd);
    return 0;
}